#pragma once
#include <Math/Complex.hpp>
#include "zMatrixBase.hpp"

namespace zzz{
//specific for complex
#define COMPLEX_EXPRESSION(name,ope) \
template<typename T,class Major>\
class zComplex##name##Expression : public zMatrixBaseR<Complex<T>,Major>\
{\
public:\
  explicit zComplex##name##Expression(const zMatrixBaseR<Complex<T>,Major>& mat)\
  :zMatrixBaseR<Complex<T>,Major>(mat.rows_,mat.cols_),Mat_(mat){}\
  using zMatrixBaseR<Complex<T>,Major>::operator(); \
  const Complex<T> operator()(zuint r, zuint c) const\
  {\
    CheckRange(r, c); \
    return ope(Mat_(r,c)); \
  }\
private:\
  const zMatrixBaseR<Complex<T>,Major> &Mat_; \
}; \
template<typename T,class Major>\
const zComplex##name##Expression<T,Major> name(const zMatrixBaseR<Complex<T>,Major> &mat)\
{\
  return zComplex##name##Expression<T,Major>(mat); \
}

template<typename T>
inline Complex<T> conj(const Complex<T> &v)
{
  return Complex<T>(v.Real(),-v.Imag());
}

COMPLEX_EXPRESSION(Conj,conj)

//complex to real
#define COMPLEX2_EXPRESSION(name,ope) \
template<typename T,class Major>\
class zComplex##name##Expression : public zMatrixBaseR<T,Major>\
{\
public:\
  explicit zComplex##name##Expression(const zMatrixBaseR<Complex<T>,Major>& mat)\
  :zMatrixBaseR<T,Major>(mat.rows_,mat.cols_),Mat_(mat){}\
  using zMatrixBaseR<Complex<T>,Major>::operator(); \
  const T operator()(zuint r, zuint c) const\
  {\
    CheckRange(r, c); \
    return ope(Mat_(r,c)); \
  }\
private:\
  const zMatrixBaseR<Complex<T>,Major> &Mat_; \
}; \
template<typename T, class Major>\
const zComplex##name##Expression<T,Major> name(const zMatrixBaseR<Complex<T>,Major> &mat)\
{\
  return zComplex##name##Expression<T,Major>(mat); \
}

template<typename T>
inline T real(const Complex<T> &v)
{
  return v.Real();
}

template<typename T>
inline T imag(const Complex<T> &v)
{
  return v.Imag();
}

COMPLEX_EXPRESSION(Real,real)
COMPLEX_EXPRESSION(Imag,imag)


}
